<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<script>
  if (window.customElements) {
    customElements.forcePolyfill = true;
  }
  window.ShadyDOM = {
    force: true
  };
</script>
<script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script src="wct-browser-config.js"></script>
<script src="../../node_modules/wct-browser-legacy/browser.js"></script>
<script type="module" src="../../polymer-legacy.js"></script>
</head>
<body>

  <dom-module id="x-event-scoped">
    <template>
      <div id="scoped" on-composed="childHandler" on-scoped="childHandler"></div>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-event-scoped',
  properties: {
    hostEvents: {
      type: Array,
      value: function() {
        return [];
      }
    },
    childEvents: {
      type: Array,
      value: function() {
        return [];
      }
    }
  },
  listeners: {
    'composed': 'hostHandler',
    'scoped': 'hostHandler'
  },
  hostHandler: function(e) {
    this.hostEvents.push({
      target: e.target,
      type: e.type,
      path: e.composedPath()
    });
  },
  childHandler: function(e) {
    this.childEvents.push({
      target: e.target,
      type: e.type,
      path: e.composedPath()
    });
  },
  fireComposed: function() {
    return this.fire('composed', null, {node: this.$.scoped});
  },
  fireScoped: function(){
    return this.fire('scoped', null, {node: this.$.scoped, composed: false});
  }
});
</script>
  </dom-module>

  <dom-module id="x-focus">
    <template>
      <style>
        :host {
          display: block;
        }
      </style>
      <div id="child" on-focus="focusHandler"></div>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-focus',
  properties: {
    events: {
      type: Array,
      value: function() {
        return [];
      }
    }
  },
  listeners: {
    focus: 'focusHandler'
  },
  focusHandler: function(e) {
    this.events.push(e.target);
  },
  fireComposed: function() {
    var ev = new Event('focus', {composed: true});
    this.$.child.dispatchEvent(ev);
  },
  fireScoped: function() {
    var ev = new Event('focus');
    this.$.child.dispatchEvent(ev);
  }
});
</script>
  </dom-module>

  <dom-module id="x-a">
    <template>
      <div id="child" on-foo="childFooHandler"></div>
      <div id="child2"></div>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-a',
  listeners: {
    'foo': 'fooHandler'
  },
  fooHandler: function(e) {
    this.event = {target: e.target, relatedTarget: e.relatedTarget};
  },
  childFooHandler: function(e) {
    this.childEvent = {target: e.target, relatedTarget: e.relatedTarget};
  }
});
</script>
  </dom-module>

  <test-fixture id="scoped">
    <template>
      <x-event-scoped></x-event-scoped>
    </template>
  </test-fixture>

  <test-fixture id="globalpatch">
    <template>
      <div></div>
    </template>
  </test-fixture>

  <test-fixture id="focus">
    <template>
      <x-focus></x-focus>
    </template>
  </test-fixture>

  <test-fixture id="relatedtarget">
    <template>
      <x-a id="one"></x-a>
      <x-a id="two"></x-a>
    </template>
  </test-fixture>

  <dom-module id="x-slot-inner">
  </dom-module>

  <test-fixture id="slot">
    <template>
      <x-slot>
        <span class="target"></span>
      </x-slot>
    </template>
  </test-fixture>

<script type="module">
suite('ShadyDOM event patching', function() {

  test('events retarget', function() {
    var el = fixture('scoped');
    el.fireComposed();
    assert.equal(el.hostEvents[0].target, el);
    assert.equal(el.childEvents[0].target, el.$.scoped);
  });

  test('event.composedPath is consistent', function() {
    var el = fixture('scoped');
    el.fireComposed();
    assert.equal(el.hostEvents.length, el.childEvents.length);
  });

  test('event patching works on non Polymer elements', function() {
    var el = fixture('globalpatch');
    var path;
    el.addEventListener('foo', function(e) {
      path = e.composedPath();
    });
    var e = new Event('foo', {composed: true});
    el.dispatchEvent(e);
    assert.deepEqual([el, el.parentNode, document.body, document.documentElement, document, window], path);
  });

  test('`composed` flag controls event propagation through roots', function() {
    var el = fixture('scoped');
    el.fireScoped();
    el.fireComposed();
    assert.equal(el.hostEvents.length, 1);
    assert.equal(el.hostEvents[0].type, 'composed');
    assert.equal(el.childEvents.length, 2);
    assert.equal(el.childEvents[0].type, 'scoped');
    assert.equal(el.childEvents[1].type, 'composed');
  });

  test('composed focus and blur events retarget up tree', function() {
    var el = fixture('focus');
    el.fireComposed();
    assert.equal(el.events.length, 2);
    assert.equal(el.events[0], el.$.child);
    assert.equal(el.events[1], el);
  });

  test('scoped focus and blur events do not retarget', function() {
    var el = fixture('focus');
    var e = new Event('focus');
    if (e.isTrused !== false) {
      // skip browser if we cannot distinguish
      // native focus events from user created ones
      this.skip();
    }
    el.fireScoped();
    assert.equal(el.events.length, 1);
    assert.equal(el.events[0], el.$.child);
  });

  test('composed relatedTarget retargets', function() {
    var els = fixture('relatedtarget');
    var a = els[0];
    var b = els[1];
    var ev = new MouseEvent('foo', {bubbles: true, composed: true, relatedTarget: b.$.child});
    a.$.child.dispatchEvent(ev);
    assert.property(a, 'childEvent');
    assert.deepEqual(a.childEvent, {target: a.$.child, relatedTarget: b});
    assert.property(a, 'event');
    assert.deepEqual(a.event, {target: a, relatedTarget: b});
  });

  test('events do not fire if relatedtarget and target are the same node after retargeting', function() {
    var els = fixture('relatedtarget');
    var a = els[0];
    var ev = new MouseEvent('foo', {bubbles: true, composed: true, relatedTarget: a.$.child2});
    a.$.child.dispatchEvent(ev);
    assert.property(a, 'childEvent');
    assert.deepEqual(a.childEvent, {target: a.$.child, relatedTarget: a.$.child2});
    assert.notProperty(a, 'event');
  });

  test('capturing event listeners fire correctly for focus and blur', function() {
    var el = fixture('focus');
    var timeStamp;
    el.addEventListener('focus', function(e) {
      timeStamp = e.timeStamp;
    }, true);
    el.fireComposed();
    assert.ok(timeStamp);
    assert.equal(el.events.length, 2);
    assert.equal(el.events[0], el.$.child);
    assert.equal(el.events[1], el);
  });

});
</script>
</body>
</html>
